﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Transactions;
using Microsoft.Practices.Unity;
using Wyn.PMS.Common.Utils;
using Wyn.PMS.Entity.Entities;
using Wyn.PMS.Entity.IDataAccess;

namespace Wyn.PMS.Common.NightAudit
{
    
    public class CustomNightAudit
    {

        private static volatile CustomNightAudit _instance;

        public static object _locker = new object();

        private IUnityContainer container = UnityContainerService.GetContainer();

        private CustomNightAudit()
        {
        }

        public static CustomNightAudit GetInstance()
        {
            if(_instance==null)
            {
                lock(_locker)
                {
                    if(_instance==null)
                    {
                        _instance = new CustomNightAudit();

                        Thread.MemoryBarrier();
                    }
                }
            }
            return _instance;
        }

      
        public bool IsAuditing(HotelBizDay bizDay)
        {
            if(bizDay.Status== EnumHelper.GetCode(NightAuditCode.Status.S) 
                ||bizDay.Status==EnumHelper.GetCode(NightAuditCode.Status.I))
            {
                return true;
            }

            return false;
        }

       
        public bool IsOverDateAudit(HotelBizDay bizDay)
        {
            IHotelBizDayRepository hotelBizdayRepository = container.Resolve<IHotelBizDayRepository>();
            
            if (bizDay.BizDay >= DateTime.Today)
            {
                return true;
            }

            string hotelId = bizDay.HotelID;

            string hostName = bizDay.HostName;

            Expression<Func<HotelBizDay, bool>> exp = b => b.HotelID == GlobalConst.ROOT_HOTEL_ID && b.HostName == hostName;

            HotelBizDay rootBizDay = hotelBizdayRepository.Find(hotelId, exp);

            if(bizDay.BizDay>=rootBizDay.BizDay)
            {
                return true;
            }

            return false;
        }

       
        public IEnumerable<Order> HasExceptionOrder(HotelBizDay bizDay)
        {

            IOrderRepository orderRepository = container.Resolve<IOrderRepository>();

            DateTime bizToday = bizDay.BizDay;

            string hotelId = bizDay.HotelID;

            string inStatus= EnumHelper.GetCode(OrderStatus.I);

          
            Expression<Func<Order, bool>> orderExp = o => o.HotelID == hotelId && o.Status == inStatus;

            IEnumerable<Order> orders = null;

            var transactionOptions = new System.Transactions.TransactionOptions();

            transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

            using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
            {
                orders = orderRepository.BatchFind(bizDay.HotelID, orderExp);

                scope.Complete();
            }

            return Precheck.CheckAuditOrders(orders,bizDay); 
        }

       
        public IEnumerable<Reservation> HasExceptionReserve(HotelBizDay bizDay)
        {
            IEnumerable<Reservation> reservations = null;

            DateTime bizToday = bizDay.BizDay;

            DateTime bizTodayMax = bizToday.AddDays(1).AddSeconds(-1);

            string hotelId = bizDay.HotelID;

            var statusR = EnumHelper.GetCode(ReserveStatus.R);

            IReserveRepository reseveRepository = container.Resolve<IReserveRepository>();

            Expression<Func<Reservation, bool>> exp = r => r.HotelID == hotelId
                                                           && r.Status == statusR 
                                                           && r.StartDate <= bizTodayMax;

            var transactionOptions = new System.Transactions.TransactionOptions();

            transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

            using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
            {
                reservations = reseveRepository.BatchFind(hotelId, exp);

                scope.Complete();
            }

            return Precheck.CheckAuditReservations(reservations, bizDay);
        }

        public IEnumerable<RoomOccupy> AutoCompleteOutOfOrder(HotelBizDay bizDay)
        {
            IRoomOccupyRepository roomOccupyRepository = container.Resolve<IRoomOccupyRepository>();

            IRoomRepository roomRepository = container.Resolve<IRoomRepository>();

            DateTime endDate = bizDay.BizDay.Date.AddDays(1).AddMinutes(-1);

            string hotelId = bizDay.HotelID;

            int repairType = (int)OrderType.RepairRoom, OOOType = (int)OrderType.OutOfOrder;

            string oooStatus = EnumHelper.GetCode(RoomStatus.OOO), vdStatus = EnumHelper.GetCode(RoomStatus.VD), vcStatus = EnumHelper.GetCode(RoomStatus.VC);

            string attrRP = EnumHelper.GetCode(RoomAttributeCode.RP);

            IList<RoomOccupy> occupies = roomOccupyRepository
                .BatchFind(hotelId, r =>
                                    r.EndDate <= endDate
                                    && r.HotelID == hotelId
                                    && (r.OrderType == repairType || r.OrderType == OOOType)
                                    && r.IsValid == true);
            return occupies;
        }

      
        public IEnumerable<RoomOccupy> AutoCompleteOutOfOrder(HotelBizDay bizDay,bool autoComplete)
        {
            IRoomOccupyRepository roomOccupyRepository = container.Resolve<IRoomOccupyRepository>();

            IRoomRepository roomRepository = container.Resolve<IRoomRepository>();

            DateTime endDate = bizDay.BizDay.Date.AddDays(1).AddMinutes(-1);

            string hotelId = bizDay.HotelID;

            int repairType = (int) OrderType.RepairRoom, OOOType = (int) OrderType.OutOfOrder;

            string oooStatus = EnumHelper.GetCode(RoomStatus.OOO), vdStatus = EnumHelper.GetCode(RoomStatus.VD),vcStatus=EnumHelper.GetCode(RoomStatus.VC);

            string attrRP = EnumHelper.GetCode(RoomAttributeCode.RP);

            IList<RoomOccupy> occupies = roomOccupyRepository
                .BatchFind(hotelId, r =>
                                    r.EndDate <= endDate
                                    && r.HotelID == hotelId
                                    && (r.OrderType == repairType || r.OrderType == OOOType)
                                    && r.IsValid == true);

            if (occupies == null || !occupies.Any()) return null;

            
            IList<string> roomNos = occupies.Select(o => o.RoomNo).ToList();

            IList<Room> rooms = roomRepository.BatchIncludeAttrFind(hotelId, r =>
                                                                             r.HotelID == hotelId
                                                                             && roomNos.Contains(r.RoomNo));

            IList<RoomAttribute> attrs = new List<RoomAttribute>();

            foreach(var room in rooms)
            {
                if(room.Status == oooStatus||room.Status==vcStatus)
                {
                    room.Status = vdStatus;
                }

                if(room.RoomAttribute!=null)
                {
                    var repair= room.RoomAttribute.FirstOrDefault(k => k.AttributeCode == attrRP && k.IsValid == true);

                    if (repair != null)
                    { 
                        repair.IsValid = false;
                        repair.EmployeeId = GlobalConst.NIGHT_AUDIT_SYSTEM;
                        repair.ModifiedTime = DateTime.Now;
                        attrs.Add(repair);
                    }
                    
                }
                room.OrderID = string.Empty;
                room.EmployeeID = GlobalConst.NIGHT_AUDIT_SYSTEM;
            }

            foreach(var occupy in occupies)
            {
                occupy.IsValid = false;

                occupy.ModifiedTime = DateTime.Now;

                occupy.EmployeeID = GlobalConst.NIGHT_AUDIT_SYSTEM;
            }

            using (TransactionScope scope = new TransactionScope())
            {
                if (attrs.Any()) roomRepository.BatchUpdateAttributes(hotelId, attrs);

                roomRepository.BatchUpdate(hotelId, rooms);

                roomOccupyRepository.BatchUpdate(hotelId, occupies);

                scope.Complete();
            }

            string roomNosExp = string.Format("'{0}'", string.Join("','", roomNos));

            roomRepository.UpdateRooms(hotelId, roomNosExp);
            
            return occupies;
        }

      
        public void DetachFromAutoNightAudit(HotelBizDay bizDay)
        {
            IHotelBizDayRepository bizDayRepository = container.Resolve<IHotelBizDayRepository>();

            bizDay.IsJoinAutoAudit = false;

            bizDayRepository.Update(bizDay);

        }

        
        public void AttachInAutoNightAudit(HotelBizDay bizDay)
        {
            IHotelBizDayRepository bizDayRepository = container.Resolve<IHotelBizDayRepository>();

            bizDay.IsJoinAutoAudit = true;

            bizDayRepository.Update(bizDay);

        }

        public void UpdateBizDayCache()
        {
            ICacheCenterRepository ccRepository = container.Resolve<ICacheCenterRepository>();

            ccRepository.UpdateCacheCenter(GlobalConst.ROOT_HOTEL_ID, GlobalConst.BIZDAY_CACHE);
        }

        public void SyncBusinessBizDay(HotelBizDay bizDay)
        {
            ISyncBizDayRepository syncRepository = container.Resolve<ISyncBizDayRepository>();

            syncRepository.Sync(new List<HotelBizDay> {bizDay});
        }

    }
}
